﻿package taurus.client;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.LinkedList;
import java.util.Queue;

import com.taurus.core.entity.ITObject;
import com.taurus.core.entity.TObject;
import com.taurus.core.util.ICallback;
import com.taurus.core.util.ByteArray;
import com.taurus.core.util.Logger;
import com.taurus.core.util.StringUtil;
import com.taurus.core.util.Utils;

import taurus.client.TaurusClient.ConnectionProtocol;



/**
 * Http网络请求处理类
 * @author daixiwei	daixiwei15@126.com
 *
 */
public class HttpClient implements ISocketLayer {
	private static final Logger	logger			= Logger.getLogger(HttpClient.class);
	private static final byte[] NULL_BYTES = new byte[0];
	private static final String	_Method			= "POST";
	private static final String _ContentTypeName = "Content-Type";
	private static final String _ContentType = "application/octet-stream";
	private static final String	_Result = "$r";
	private static final String _Param = "$p";
	private static final String _Session = "$s";
	private static final String _Version = "$v";
	String						_url;
	String						_session_id		= "";

	final Queue<Runnable>		_actionQueue	= new LinkedList<Runnable>();
	

	public HttpClient(String url) {
		_url = url;
	}

	private void __sendMsg(Message msg) {
		byte[] bytes = NULL_BYTES;
		ITObject _data = TObject.newInstance();
		_data.putString(_Session, _session_id);
		_data.putInt(_Version, NetManager.VERSION);
		if (msg.useText && msg.param == null) {
			if (StringUtil.isEmpty(msg.textParam)) {
				msg.param = TObject.newInstance();
			} else {
				try {
					msg.param = TObject.newFromJsonData(msg.textParam);
				} catch (Exception e) {
					msg.param = TObject.newInstance();
				}
			}
		}
		_data.putTObject(_Param, msg.param);
		ByteArray buff = new ByteArray(_data.toBinary());
		try {
			buff.compress();
		} catch (Exception e) {
			logger.error(e);
		}
		bytes = buff.bytes();

		__requestAsync(msg, bytes);
	}

	private void __requestAsync(final Message msg,byte[] bytes) {
		OutputStream out = null;
		InputStream in = null;
		int code = 500;
		HttpURLConnection conn = null;
		try {

			URL realUrl = new URL(_url + msg.command);
			conn = (HttpURLConnection) realUrl.openConnection();
			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.setRequestProperty(_ContentTypeName, _ContentType); 
			conn.setUseCaches(false);// Post 请求不能使用缓存
			conn.setRequestMethod(_Method);
			conn.setConnectTimeout(NetManager.TIMEOUT_TIME * 1000);
			conn.setReadTimeout(NetManager.TIMEOUT_TIME * 1000);
			conn.connect();
			
			
			out = conn.getOutputStream();
			out.write(bytes);
			if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
				in = conn.getInputStream(); 
				bytes = Utils.uncompress(in);
				if (bytes!=null) {
					
					ITObject tem = TObject.newFromBinaryData(bytes);
					int result = tem.getInt(_Result);

					if (result == 0) {
						ITObject _param = tem.getTObject(_Param);
						msg.param = _param;
						if(msg.useText) {
							msg.textParam = _param.toJson();
						}
					}
					code = result;
					
				}
			}
		} catch (SocketTimeoutException e) {
			logger.error(e);
			code = 102;
		} catch (ConnectException ex1) {
			logger.error(ex1);
			code = 101;
		} catch (Exception ex2) {
			logger.error(ex2);
			code = 500;
		}finally {
			try {
				if (out != null) {
					out.close();
				}
			} catch (IOException ex) {
				logger.error(ex);
			}
			try {
				if (in != null) {
					in.close();
				}
			}catch (IOException ex) {
				logger.error(ex);
			}
			if(conn!=null) {
				conn.disconnect();
			}
		}
		if (msg.callback != null) {
			final MessageResponse response = new MessageResponse();
			response.messageData = msg;
			response.returnCode = code;
			enqueueActionForDispatch(new Runnable() {
				@Override
				public void run() {
					msg.callback.action(response);
				}
			});
		}

	}

	@Override
	public void connect() {

	}
	
	@Override
	public void connect(ITObject validate) {

	}

	@Override
	public void disconnect() {

	}

	private void enqueueActionForDispatch(Runnable action) {
		synchronized (_actionQueue) {
			_actionQueue.offer(action);
		}
	}

	@Override
	public void processEvent() {
		while (true) {
			Runnable myAction;
			synchronized (_actionQueue) {
				if (_actionQueue.size() <= 0) {
					break;
				}
				myAction = _actionQueue.poll();
			}
			myAction.run();
		}
	}

	public void write(final Message msg) {
		new Thread(new Runnable() {
			@Override
			public void run() {
				__sendMsg(msg);
			}
		}).start();
	}

	public int getAveragePingTime() {
		return 1000;
	}

	public void setSession(String session) {
		_session_id = session == null ? StringUtil.Empty : session;
	}

	public String getSession() {
		return _session_id;

	}

	@Override
	public void setOnConnect(ICallback<SocketCode> onConnect) {

	}

	@Override
	public void setOnEvent(ICallback<Message> onEvent) {

	}

	@Override
	public ConnectionProtocol getProtocol() {
		return ConnectionProtocol.Web;
	}
}
